package com.fingard.net.listener;

import sun.misc.BASE64Decoder;

import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.File;
import java.util.Date;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.Map;
import java.util.concurrent.atomic.AtomicInteger;

import com.fingard.FGBiz;
import com.fingard.constant.Format;
import com.fingard.diagnostics.LogHelper;
import com.fingard.dsp.bank.directConfig.ListenerSet;
import com.fingard.dsp.bank.directbank.DirectBase;
import com.fingard.dsp.bank.directbank.bankInterface.IAcceptNotice;
import com.fingard.dsp.bank.reqrespobj.ReqRespHeader;
import com.fingard.dsp.tcpserver.RequestNIO;
import com.fingard.generalobj.NoticeReq;
import com.fingard.generalobj.NoticeResp;
import com.fingard.io.FileHelper;
import com.fingard.net.niosocket.HttpNioReceiver;
import com.fingard.text.StringHelper;
import com.fingard.util.DateHelper;
import com.fingard.dsp.tcpserver.SocketReceiver;
import com.fingard.dsp.web.HttpContext;
import com.fingard.dsp.web.HttpResponse;
import com.fingard.dsp.web.HttpRunnable;


public class HttpThread implements Runnable {


    //private String bankCode;
    private HttpReceiver reqSocketReceiver;
    HashMap<String, ListenerSet> mapForBankClass = null;

    public HttpThread(HttpReceiver p_SocketReceiver, HashMap<String, ListenerSet> p_map) {
        reqSocketReceiver = p_SocketReceiver;
        mapForBankClass = p_map;
    }

    public void run() {
        try {
            doRequest();
        } catch (Exception ex) {
            FGBiz.limitMsg.exception("HttpThread", ex);
        }

    }

    private static AtomicInteger concurrentNum = new AtomicInteger();
    private static int maxConcurrent = 0;
    private static Date maxHappenedTime = new Date();
    
    private static AtomicInteger concurrentNumDspCmd = new AtomicInteger();
    private static int maxConcurrentDspCmd = 0;
    private static Date maxHappenedTimeDspCmd = new Date();
    
    /**获取并发数信息*/
    public static Map<String,String> getConcurrentNum(){
    	Map<String,String> retMap = new LinkedHashMap<String,String>();
    	retMap.put("concurrentNumDspCmd", concurrentNumDspCmd+"");
    	retMap.put("maxConcurrentDspCmd", maxConcurrentDspCmd+"");
    	retMap.put("maxHappenedTimeDspCmd", Format.DateTimeFormat.format(maxHappenedTimeDspCmd));
    	
    	retMap.put("concurrentNum", concurrentNum+"");
    	retMap.put("maxConcurrent", maxConcurrent+"");
    	retMap.put("maxHappenedTime", Format.DateTimeFormat.format(maxHappenedTime));
        return retMap;
    }

    private void doRequest() {

        int tmpConcurrentNum = concurrentNum.incrementAndGet();
        if (tmpConcurrentNum > maxConcurrent) {
            maxConcurrent = tmpConcurrentNum;
            maxHappenedTime = new Date();
        }

        DataOutputStream tmpRespOutput = null;
        DataInputStream tmpReqInput = null;

        StringBuilder sbLog = new StringBuilder();
        sbLog.append(Format.DateTimeSSSFormat.format(reqSocketReceiver.connTime) + " 开始接收\r\n");
        String visitPage = "";
        String oriVstPage = "";
        String bankCode = "null";
        boolean isDsp = false;
        try {
            tmpReqInput = new DataInputStream(reqSocketReceiver.socketClient.getInputStream());
            reqSocketReceiver.recBytes = new byte[10240];//10K
            int tmpOffSet = 0;
            int idxLastCrlf = 0;
            String httpMethod = "";
            byte tmpByteCr = "\r".getBytes(reqSocketReceiver.initCharset)[0];
            byte tmpByteLf = "\n".getBytes(reqSocketReceiver.initCharset)[0];
            while (true) {

                int tmpReadLen = tmpReqInput.read(reqSocketReceiver.recBytes, tmpOffSet, reqSocketReceiver.recBytes.length - tmpOffSet);
                sbLog.append(Format.DateTimeSSSFormat.format(reqSocketReceiver.connTime) + " 读取字节数：" + tmpReadLen + "\r\n");
                if (tmpReadLen <= 0) {
                    break;
                }
                reqSocketReceiver.readedLength += tmpReadLen;
                if (reqSocketReceiver.contentStartIndex == -1) {
                    for (int i = idxLastCrlf; i < reqSocketReceiver.readedLength; i++) {
                        if (reqSocketReceiver.recBytes[i] == tmpByteLf) {//是\n
                        	int tmpEndCount = i - idxLastCrlf;
                            if (i > 1 && reqSocketReceiver.recBytes[i - 1] == tmpByteCr){// 前一个字符是\r
                            	tmpEndCount--;
                            }
                            String tmpHeadStr = new String(reqSocketReceiver.recBytes, idxLastCrlf, tmpEndCount, reqSocketReceiver.initCharset);
                            String tmpKey = "";
                            String tmpValue = "";
                            
                            int idx1 = tmpHeadStr.indexOf(":");
                            if(idx1>0){
                            	tmpKey = tmpHeadStr.substring(0,idx1).toLowerCase().trim();
                            	tmpValue = tmpHeadStr.substring(idx1+1).toLowerCase().trim();
                            }else{
                            	tmpKey = tmpHeadStr.toLowerCase().trim();
                            }
                            reqSocketReceiver.headKV.put(tmpKey, tmpValue);
                            if ("content-length".equals(tmpKey)) {
                            	reqSocketReceiver.contentLength = Integer.valueOf(tmpValue);
                            }
                            idxLastCrlf = i + 1;
                            ////POST /notify HTTP/1.1
                            if (StringHelper.isNullOrEmpty(visitPage)) {
                                String headPage = tmpHeadStr.toLowerCase();
                                if (headPage.startsWith("post") || headPage.startsWith("get") || headPage.startsWith("head")) {
                                    String[] pValues = headPage.split(" ");
                                    httpMethod = pValues[0].toLowerCase();
                                    if (pValues.length > 1) {
                                        visitPage = pValues[1];
                                    }
                                }
                            }
                            
                            //if(reqSocketReceiver.recBytes[i - 1] == tmpByteCr && reqSocketReceiver.recBytes[i] == tmpByteLf){
                            //	
                            //}

                            if ((i < tmpReadLen - 1 && reqSocketReceiver.recBytes[i + 1] == tmpByteCr) ||
                                    (i < tmpReadLen - 2 && reqSocketReceiver.recBytes[i + 2] == tmpByteLf)) {
                            	reqSocketReceiver.contentStartIndex = i+1;
                                if(reqSocketReceiver.recBytes[i + 2] == tmpByteLf){
                                	reqSocketReceiver.contentStartIndex = i+3;
                                }
                                if("get".equals(httpMethod)){
                                	if(reqSocketReceiver.contentLength<0){
                                		reqSocketReceiver.contentLength = 0;
                                	}
                                }
                                break;
                            }
                        }
                    }
                }
                tmpOffSet += tmpReadLen;

                if (reqSocketReceiver.contentLength > 0 && reqSocketReceiver.contentStartIndex > 0) {
                    if (reqSocketReceiver.recBytes.length < reqSocketReceiver.contentLength + reqSocketReceiver.contentStartIndex) {
                        byte[] tmpUnionInfo = new byte[reqSocketReceiver.contentLength + reqSocketReceiver.contentStartIndex];//一次性分配内存
                        System.arraycopy(reqSocketReceiver.recBytes, 0, tmpUnionInfo, 0, tmpOffSet);
                        reqSocketReceiver.recBytes = null;
                        reqSocketReceiver.recBytes = tmpUnionInfo;
                    }

                    int tmpToReadLen = reqSocketReceiver.contentLength + reqSocketReceiver.contentStartIndex - tmpOffSet;
                    if (tmpToReadLen <= 0) {
                        break;
                    }
                }else if(reqSocketReceiver.readedLength>0&& (reqSocketReceiver.readedLength<20 || reqSocketReceiver.contentStartIndex<0)){
                	continue;
                }  else {
                    break;
                }
            }
            reqSocketReceiver.recCompleteTime = new Date();

            if (reqSocketReceiver.readedLength == 0) {
                //F5等设备的监测
                tmpReqInput.close();
                tmpReqInput = null;
                reqSocketReceiver.socketClient.close();
                return;
            }

            //String strFull = reqSocketReceiver.getFullString(reqSocketReceiver.initCharset);
            //System.out.println("-------\r\n"+strFull+"\r\n-----");

            //String tmpStrReq = new String(tmpReceiver.reqBytes,tmpReceiver.contentStartIndex,tmpReceiver.contentLength,tmpReceiver.charsetName);
            //MyHandler tmpHandler = new MyHandler();
            visitPage = StringHelper.trimEnd(visitPage, "/");
            if (StringHelper.isNullOrEmpty(visitPage)) {
                visitPage = "/";
            }
            oriVstPage = visitPage;
            String reqCharset = reqSocketReceiver.initCharset;
            boolean isMatchPage = false;
            if (mapForBankClass.containsKey(visitPage)) {
                reqCharset = mapForBankClass.get(visitPage).charset;
                isMatchPage = true;
            }else{
            	int lastIdx = visitPage.lastIndexOf("/");
            	if(lastIdx>0){
            		String v2 = visitPage.substring(0, lastIdx);
            		if (mapForBankClass.containsKey(v2)) {
            			visitPage = v2;
                        reqCharset = mapForBankClass.get(visitPage).charset;
                    }
            	}
            	if(!isMatchPage){
            		lastIdx = visitPage.lastIndexOf("?");
                	if(lastIdx>0){
                		String v3 = visitPage.substring(0, lastIdx);
                		v3 = StringHelper.trimEnd(v3, "/");
                		if (mapForBankClass.containsKey(v3)) {
                			visitPage = v3;
                            reqCharset = mapForBankClass.get(visitPage).charset;
                        }
                	}
            	}
            }

            String tmpRespHead = "";
            String tmpRespBody = "";
            byte[] tmpBytesBody = null;
            
            if (mapForBankClass != null && mapForBankClass.containsKey(visitPage)) {
                bankCode = mapForBankClass.get(visitPage).bankFront.bankCode;
                
                if("dsp".equals(bankCode)){
                	
                    int numDspCmd = concurrentNumDspCmd.incrementAndGet();
                    if (numDspCmd > maxConcurrentDspCmd) {
                        maxConcurrentDspCmd = numDspCmd;
                        maxHappenedTimeDspCmd = new Date();
                    }
                	try{
                	
	                	SocketReceiver socketRec = new SocketReceiver(reqSocketReceiver.socketClient, reqCharset);
	                	socketRec.readedLength = reqSocketReceiver.readedLength;
	                	socketRec.isCompleted = true;
	                	socketRec.isHttp = true;
	//                	socketRec.isReqEncrypt ="true".equals(reqSocketReceiver.headKV.get("encrypt"))?true:false;
	                	socketRec.isReqSign = "true".equals(reqSocketReceiver.headKV.get("content-sign"))?true:false;
	                	socketRec.contentLength = reqSocketReceiver.contentLength;
	                	socketRec.contentStartIndex = reqSocketReceiver.contentStartIndex;
	                	socketRec.reqBytes = reqSocketReceiver.recBytes;
	                	socketRec.charsetName = reqCharset;
	                	
	                	RequestNIO reqNio = new RequestNIO(socketRec);
	                	reqNio.seqIDInner = reqSocketReceiver.seqIDInner;
	                	reqNio.isInvokeByHttp = true;
	                	reqNio.connTime = reqSocketReceiver.connTime;
	                	reqNio.recCompleteTime = reqSocketReceiver.recCompleteTime;
	                	reqNio.GetReqRespHeader();
			            FGBiz.bankServer.addRequest(reqNio);
			            isDsp = true;
			            return;
		            
                	}catch(Exception ex){
                		tmpRespBody = LogHelper.getStackTraceFirstLine(ex);
                		FGBiz.limitMsg.exception("HttpThread.dsp", ex, reqSocketReceiver.getFullString(reqCharset));
                	}finally{
                		numDspCmd = concurrentNumDspCmd.decrementAndGet();
                        if (numDspCmd == 0) {
                        	if(DateHelper.getDifferMins(new Date(), maxHappenedTimeDspCmd) > 30){
                        		maxConcurrentDspCmd = 0;
                        	}
                        }
                	}
                }
                
                String className = "com.fingard.dsp.bank.directbank." + bankCode.toLowerCase() + "." + bankCode + "AcceptNotice";
                Class<?> bankClass = Class.forName(className);
                if (bankClass != null) {
                    DirectBase tmpDirectBase = (DirectBase) bankClass.newInstance();
                    tmpDirectBase.reqHeader = new ReqRespHeader();
                    tmpDirectBase.reqHeader.bankCode = bankCode;
                    tmpDirectBase.bankFront = mapForBankClass.get(visitPage).bankFront;
                    tmpDirectBase.setSbLog(sbLog);
                    IAcceptNotice iNotice = (IAcceptNotice) tmpDirectBase;
                    
                    NoticeReq req = new NoticeReq();
                    req.reqCharset = reqCharset;
                    req.contentStartIndex = reqSocketReceiver.contentStartIndex;
                    req.contentLength = reqSocketReceiver.contentLength;
                    req.reqBytes = new byte[reqSocketReceiver.readedLength];
                    System.arraycopy(reqSocketReceiver.recBytes, 0, req.reqBytes, 0, reqSocketReceiver.readedLength);
                    NoticeResp ntResp = iNotice.dealNotice(req);
                    if(ntResp.respBytes!=null){
                    	tmpRespBody = new String(ntResp.respBytes,0,ntResp.respBytes.length,reqCharset);
                    }
                    /**
                     * HTTP/1.1 200 OK
                     Server: nginx/1.7.4
                     Date: Sat, 18 Mar 2017 02:40:17 GMT
                     Content-Length: 226536
                     Connection: keep-alive
                     * */
                    tmpRespHead = "HTTP/1.1 200 OK\r\n";
                }
            } else {
            	if("head".equals(httpMethod)){
            		tmpRespHead = "HTTP/1.1 200 OK\r\n";
            		tmpRespBody = "DSP HTTP Listener";
            	}else{
            		HttpNioReceiver nioHttpReceiver = new HttpNioReceiver(null, reqCharset);
            		nioHttpReceiver.headKV = reqSocketReceiver.headKV;
            		nioHttpReceiver.visitPage = oriVstPage;
            		nioHttpReceiver.recBytes = reqSocketReceiver.recBytes;
            		nioHttpReceiver.contentStartIndex = reqSocketReceiver.contentStartIndex;
            		nioHttpReceiver.contentLength = reqSocketReceiver.contentLength;
            		nioHttpReceiver.readedLength = reqSocketReceiver.readedLength;
            		HttpContext httpContext = new HttpContext(nioHttpReceiver);
            		httpContext.response.respCharset = reqCharset;
            		
            		HttpRunnable runPage = new HttpRunnable();
            		HttpResponse resp = runPage.loadPage(httpContext);
            		if(resp.isRtnBytes){
            			tmpBytesBody = resp.respBytes;
            		}else{
            			tmpRespBody = resp.respMsg;
            		}
            		tmpRespHead = "HTTP/1.1 200 OK\r\n"+resp.respHead.toString();
            	}
            }


            tmpRespOutput = new DataOutputStream(reqSocketReceiver.socketClient.getOutputStream());
            
            if(tmpBytesBody==null){
            	if (bankCode.equals("CCB04")) {//防止数据丢失做base64转换，防止des解密失败（字节数转换过程中被改变）
                    tmpBytesBody = new BASE64Decoder().decodeBuffer(tmpRespBody);
                } else {
                    tmpBytesBody = tmpRespBody.getBytes(reqCharset);
                }	
            }

            String tmpStrLen = "" + tmpBytesBody.length;
            tmpRespHead = tmpRespHead + "Content-Length: " + tmpStrLen + "\r\nConnection: close\r\n\r\n";

            byte[] tmpBytesHead = tmpRespHead.getBytes(reqCharset);

            byte[] tmpBytesResp = new byte[tmpBytesHead.length + tmpBytesBody.length];
            System.arraycopy(tmpBytesHead, 0, tmpBytesResp, 0, tmpBytesHead.length);
            System.arraycopy(tmpBytesBody, 0, tmpBytesResp, tmpBytesHead.length, tmpBytesBody.length);

            if(StringHelper.hasAnyChar(tmpRespBody)){
            	sbLog.append(Format.DateTimeSSSFormat.format(new Date()) + " 响应：" + tmpRespHead + tmpRespBody + "\r\n");
            }

            tmpRespOutput.write(tmpBytesResp);

            tmpReqInput.close();
            tmpReqInput = null;
            tmpRespOutput.close();
            tmpRespOutput = null;
            //System.out.println("处理完"+p_receiver.seqIDInner);


        } catch (Exception e) {
            sbLog.append(LogHelper.getStackTrace(e) + "\r\n");
        } finally {

            try {

            	if(!isDsp){
	                if (tmpReqInput != null) {
	                    tmpReqInput.close();
	                }
	                if (tmpRespOutput != null) {
	                    tmpRespOutput.close();
	                }
	                reqSocketReceiver.socketClient.close();
            	}
                tmpConcurrentNum = concurrentNum.decrementAndGet();
                if (tmpConcurrentNum == 0) {
                	if(DateHelper.getDifferMins(new Date(), maxHappenedTime) > 30){
                		maxConcurrent = 0;
                	}
                }

            } catch (Exception e) {
                sbLog.append(LogHelper.getStackTrace(e) + "\r\n");
            }
        }

        try {
            Date finishTime = new Date();
            //String tmpStrLogSeq = String.format("%04d", reqSocketReceiver.seqIDInner);
            String tmpDateDir = Format.DateForLogFormat.format(finishTime).replace("-", File.separator);

            String tmpSubFileName = "ListenerLogs_" + reqSocketReceiver.ip + File.separator + tmpDateDir + File.separator + bankCode + "_" + reqSocketReceiver.port + File.separator + bankCode + "_" + Format.DateForLogFormat.format(finishTime) + ".txt";

            String tmpLogFile = FGBiz.dspCenter.bankLogsDirPath + File.separator + tmpSubFileName;
            sbLog.append("\r\n");

            FileHelper.appendFile(tmpLogFile, sbLog.toString(), FGBiz.dspCenter.charset);
        } catch (Exception ex) {
        	FGBiz.limitMsg.exception("HttpThread."+bankCode, ex);
        }

    }
}
